package ccm.communicate.serial;

import ccm.component.override.VFlowLayout;
import com.fazecast.jSerialComm.SerialPort;

import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class SerialConfigJPanel extends JPanel implements ActionListener, PopupMenuListener
{
    /**
     * 保存被控制的串口
     */
    protected final Serial                serial;
    /**
     * 串口号下拉菜单
     */
    protected final JComboBox<SerialPort> serialPortJComboBox;
    /**
     * 波特率下拉菜单
     */
    protected final JComboBox<Integer>    baudRateJComboBox;
    /**
     * 校验位下拉菜单
     */
    protected final JComboBox<Integer>    parityJComboBox;
    /**
     * 数据位下拉菜单
     */
    protected final JComboBox<Integer>    dataBitsJComboBox;
    /**
     * 停止位下拉菜单
     */
    protected final JComboBox<Integer>    stopBitsJComboBox;

    SerialConfigJPanel(Serial serialIn) throws NullPointerException
    {
        super();
        if(serialIn==null)
            throw new NullPointerException();
        this.serial=serialIn;
        setLayout(new VFlowLayout());
        setBorder(new TitledBorder("串口设置"));
        /*
          生成串口号的面板
         */
        JPanel serialPortJComboBoxJPanel=new JPanel();
        add(serialPortJComboBoxJPanel);
        serialPortJComboBoxJPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        serialPortJComboBoxJPanel.add(new JLabel("串口号"));
        serialPortJComboBox=new JComboBox<>();
        serialPortJComboBoxJPanel.add(serialPortJComboBox);
        serialPortJComboBox.addActionListener(this);
        serialPortJComboBox.addPopupMenuListener(this);
        serialPortJComboBox.setRenderer((list,value,index,isSelected,cellHasFocus)->{
            JLabel jLabel=new JLabel();
            if(value!=null)
                jLabel.setText(value.getSystemPortName());
            jLabel.setOpaque(true);
            return jLabel;
        });
        /*
          生成波特率的面板
          可编辑
         */
        JPanel baudRateJComboBoxJPanel=new JPanel();
        this.add(baudRateJComboBoxJPanel);
        baudRateJComboBoxJPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        baudRateJComboBoxJPanel.add(new JLabel("波特率"));
        baudRateJComboBox=new JComboBox<>(new Integer[]{9600,115200,460800,2000000,3000000});
        baudRateJComboBoxJPanel.add(baudRateJComboBox);
        baudRateJComboBox.addActionListener(this);
        baudRateJComboBox.setEditable(true);
        /*
          生成校验位的面板
         */
        JPanel parityJComboBoxJPanel=new JPanel();
        this.add(parityJComboBoxJPanel);
        parityJComboBoxJPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        parityJComboBoxJPanel.add(new JLabel("校验位"));
        parityJComboBox=new JComboBox<>(new Integer[]{SerialPort.NO_PARITY,SerialPort.ODD_PARITY,SerialPort.EVEN_PARITY});
        parityJComboBoxJPanel.add(parityJComboBox);
        parityJComboBox.addActionListener(this);
        parityJComboBox.setRenderer((list,value,index,isSelected,cellHasFocus)->{
            JLabel jLabel=new JLabel();
            switch(value)
            {
                case SerialPort.NO_PARITY:
                    jLabel.setText("NONE");
                    break;
                case SerialPort.ODD_PARITY:
                    jLabel.setText("ODD");
                    break;
                case SerialPort.EVEN_PARITY:
                    jLabel.setText("EVEN");
                    break;
            }
            jLabel.setOpaque(true);
            return jLabel;
        });
        /*
          生成数据位的面板
         */
        JPanel dataBitsJComboBoxJPanel=new JPanel();
        this.add(dataBitsJComboBoxJPanel);
        dataBitsJComboBoxJPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        dataBitsJComboBoxJPanel.add(new JLabel("数据位"));
        dataBitsJComboBox=new JComboBox<>(new Integer[]{8});
        dataBitsJComboBoxJPanel.add(dataBitsJComboBox);
        dataBitsJComboBox.addActionListener(this);
        /*
          生成停止位的面板
         */
        JPanel stopBitsJComboBoxJPanel=new JPanel();
        this.add(stopBitsJComboBoxJPanel);
        stopBitsJComboBoxJPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        stopBitsJComboBoxJPanel.add(new JLabel("停止位"));
        stopBitsJComboBox=new JComboBox<>(new Integer[]{SerialPort.ONE_STOP_BIT,SerialPort.ONE_POINT_FIVE_STOP_BITS,SerialPort.TWO_STOP_BITS});
        stopBitsJComboBoxJPanel.add(stopBitsJComboBox);
        stopBitsJComboBox.addActionListener(this);
        stopBitsJComboBox.setRenderer((list,value,index,isSelected,cellHasFocus)->{
            JLabel jLabel=new JLabel();
            switch(value)
            {
                case SerialPort.ONE_STOP_BIT:
                    jLabel.setText("1");
                    break;
                case SerialPort.ONE_POINT_FIVE_STOP_BITS:
                    jLabel.setText("1.5");
                    break;
                case SerialPort.TWO_STOP_BITS:
                    jLabel.setText("2");
                    break;
            }
            jLabel.setOpaque(true);
            return jLabel;
        });
        /*
          刷新串口号
          更新大小
          并设置所有的默认选项
         */
        updateSerialPortJComboBox(serial.getSerialPort());
        updatePreferredSize();
        baudRateJComboBox.setSelectedItem(serial.getBaudRate());
        parityJComboBox.setSelectedItem(serial.getParity());
        dataBitsJComboBox.setSelectedItem(serial.getDataBits());
        stopBitsJComboBox.setSelectedItem(serial.getStopBits());
        new Thread(()->{
            try
            {
                while(true)
                {
                    if(serialPortJComboBox.getItemCount()!=SerialPort.getCommPorts().length&&!serial.isOpen())
                        updateSerialPortJComboBox();
                    Thread.sleep(100);
                }
            }catch(InterruptedException e)
            {
                e.printStackTrace();
            }
        }).start();
    }

    /**
     * 更新串口号下拉菜单
     */
    private void updateSerialPortJComboBox(SerialPort s)
    {
        serialPortJComboBox.removeAllItems();
        for(SerialPort serialPort: SerialPort.getCommPorts())
        {
            serialPortJComboBox.addItem(serialPort);
            if(s!=null&&s.getSystemPortName().equals(serialPort.getSystemPortName()))
                serialPortJComboBox.setSelectedItem(serialPort);
        }
    }

    /**
     * 更新各个面板的宽度
     */
    private void updatePreferredSize()
    {
        int width=serialPortJComboBox.getPreferredSize().width;
        width=Math.max(width,baudRateJComboBox.getPreferredSize().width);
        width=Math.max(width,parityJComboBox.getPreferredSize().width);
        width=Math.max(width,dataBitsJComboBox.getPreferredSize().width);
        width=Math.max(width,stopBitsJComboBox.getPreferredSize().width);
        serialPortJComboBox.setPreferredSize(new Dimension(width,serialPortJComboBox.getPreferredSize().height));
        baudRateJComboBox.setPreferredSize(serialPortJComboBox.getPreferredSize());
        parityJComboBox.setPreferredSize(serialPortJComboBox.getPreferredSize());
        dataBitsJComboBox.setPreferredSize(serialPortJComboBox.getPreferredSize());
        stopBitsJComboBox.setPreferredSize(serialPortJComboBox.getPreferredSize());
    }

    /**
     * 更新串口号下拉菜单
     */
    private void updateSerialPortJComboBox()
    {
        updateSerialPortJComboBox((SerialPort)serialPortJComboBox.getSelectedItem());
    }

    /**
     * 动作事件
     * 处理内部所有面板的触发事件
     */
    public void actionPerformed(ActionEvent e)
    {
        if(e.getSource().equals(serialPortJComboBox))
        {
            serial.setSerialPort((SerialPort)serialPortJComboBox.getSelectedItem());
        }
        else if(e.getSource().equals(baudRateJComboBox))
        {
            try
            {
                serial.setBaudRate(getSelectInteger(baudRateJComboBox));
            }catch(NullPointerException ex)
            {
                JOptionPane.showMessageDialog(this,"不合法的波特率","错误",JOptionPane.ERROR_MESSAGE);
                ex.printStackTrace();
            }
        }
        else if(e.getSource().equals(parityJComboBox))
        {
            try
            {
                serial.setParity(getSelectInteger(parityJComboBox));
            }catch(NullPointerException ex)
            {
                ex.printStackTrace();
            }
        }
        else if(e.getSource().equals(dataBitsJComboBox))
        {
            try
            {
                serial.setDataBits(getSelectInteger(dataBitsJComboBox));
            }catch(NullPointerException ex)
            {
                ex.printStackTrace();
            }
        }
        else if(e.getSource().equals(stopBitsJComboBox))
        {
            try
            {
                serial.setStopBits(getSelectInteger(stopBitsJComboBox));
            }catch(NullPointerException ex)
            {
                ex.printStackTrace();
            }
        }
    }

    /**
     * 获取下拉菜单选中的整数
     *
     * @param jComboBox 欲获取的下拉菜单
     * @return 整数
     * @throws NullPointerException 获取失败时抛出NullPointerException
     */
    private Integer getSelectInteger(JComboBox<Integer> jComboBox) throws NullPointerException
    {
        Object obj=jComboBox.getSelectedItem();
        if(obj instanceof Integer)
            return (Integer)obj;
        throw new NullPointerException();
    }

    /**
     * 在下拉菜单弹出前更新串口号
     */
    public void popupMenuWillBecomeVisible(PopupMenuEvent e)
    {
        if(e.getSource().equals(serialPortJComboBox))
        {
            updateSerialPortJComboBox();
            updatePreferredSize();
        }
    }

    public void popupMenuWillBecomeInvisible(PopupMenuEvent e)
    {

    }

    public void popupMenuCanceled(PopupMenuEvent e)
    {

    }
}
